#pragma once

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

namespace NetWorkModule{
    const int defaultsockfd = -1;
    const int backlog = 5; // 定义全连接队列的长度

    enum {
        SocketError = 1,
        BindError,
        ListenError
    };

    class TcpSocket{
        public:
            TcpSocket(int sockfd = defaultsockfd) :_sockfd(sockfd) {}
            ~TcpSocket() {}

            void CreateSocket() {
                _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
                if(_sockfd < 0) 
                    exit(SocketError);
            }

            void BindSocket(uint16_t port) {
                struct sockaddr_in local;
                memset(&local, 0, sizeof(local));
                local.sin_family = AF_INET;
                local.sin_addr.s_addr = INADDR_ANY;
                local.sin_port = htons(port);

                int n = ::bind(_sockfd, (struct sockaddr*)(&local), sizeof(local));
                if(n < 0)
                    exit(BindError);
            }

            void ListenSocket(int backlog) {
                int n = ::listen(_sockfd, backlog);
                if(n < 0) 
                    exit(ListenError);
            }

            int AcceptConnection(std::string *peerip, uint16_t *peerport) {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int newsockfd = ::accept(_sockfd, (struct sockaddr*)(&peer), &len);
                if(newsockfd < 0) 
                    return -1;
                
                *peerip = inet_ntoa(peer.sin_addr);
                *peerport = ntohs(peer.sin_port);
                return newsockfd; 
            }

            bool ConnectServer(std::string &serverip, uint16_t serverport) {
                struct sockaddr_in server;
                memset(&server, 0, sizeof(server));
                server.sin_family = AF_INET;
                server.sin_addr.s_addr = inet_addr(serverip.c_str());
                server.sin_port = htons(serverport);

                int n = ::connect(_sockfd, (struct sockaddr*)(&server), sizeof(server));
                if(n == 0)
                    return true;
                return false;
            }

            void CloseSocket() {
                if(_sockfd > defaultsockfd)
                    ::close(_sockfd);
            }

            int GetSockfd() {
                return _sockfd;
            }
        private:
            int _sockfd;
    };
}